home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume6 / help / part1 next >
Encoding:
Internet Message Format  |  1986-11-30  |  40.9 KB

  1. Subject: v06i045:  Help programs (help), Part1/2
  2. Newsgroups: mod.sources
  3. Approved: rs@mirror.UUCP
  4.  
  5. Submitted by: talcott!seismo!wucs!nz (Neal Ziring)
  6. Mod.sources: Volume 6, Issue 45
  7. Archive-name: help/Part1
  8.  
  9. [  This first part contains program and documentation; the next
  10.    part contains a small documentation tree.  --r$ ]
  11.  
  12. #! /bin/sh
  13. # This is a shell archive, meaning:
  14. # 1. Remove everything above the #! /bin/sh line.
  15. # 2. Save the resulting text in a file.
  16. # 3. Execute the file with /bin/sh (not csh) to create the files:
  17. #    Makefile
  18. #    README
  19. #    help.1
  20. #    help.h
  21. #    help.c
  22. # This archive created: Tue Jun  3 08:20:04 1986
  23. # By:    Neal Ziring (Washington U. Engineering Computer Lab)
  24. export PATH; PATH=/bin:$PATH
  25. if test -f 'Makefile'
  26. then
  27.     echo shar: will not over-write existing file "'Makefile'"
  28. else
  29. cat << \SHAR_EOF > 'Makefile'
  30. #
  31. #  help Makefile, 1.1    7/27/84
  32. #
  33. CFLAGS= -O
  34. SRCS= help.h help.c
  35. DESTDIR=
  36.  
  37. help:    help.o 
  38.     ${CC} ${CFLAGS} help.o -o help
  39.  
  40. install: help
  41.     install -s -c help $(DESTDIR)/usr/local/bin
  42.  
  43. help.o: help.h /usr/include/sys/file.h /usr/include/sys/param.h
  44.  
  45. ci:
  46.     ci -l ${SRCS}
  47.  
  48. clean:  
  49.     rm -f *.o help
  50.  
  51. SHAR_EOF
  52. fi # end of overwriting check
  53. if test -f 'README'
  54. then
  55.     echo shar: will not over-write existing file "'README'"
  56. else
  57. cat << \SHAR_EOF > 'README'
  58.  
  59.  Help is a facility for climbing a tree structure.
  60.  
  61.  Help comes in two types: a help topic with subtopics, or a help topic
  62.  without any subtopics.  
  63.  
  64.     Topics that have no subtopics reside in the directory they are
  65.     a subtopic of, with filenames `topicname.HLP'
  66.  
  67.     Topics that have subtopics of their own reside in directories
  68.     which are subdirectories of the help topic of which they are
  69.     subtopics, and have filenames `.HLP'  Thier subtopics have the
  70.     filenames `topicname.HLP'.  A directory for a subtopic just
  71.     has the subtopic's name as its directory name.
  72.  
  73.     For instance, if you had a help topic "ls" with some subtopics,
  74.     /usr/help/ls would be a directory, containing the files
  75.  
  76.         /usr/help/ls/.HLP    main help text
  77.         /usr/help/ls/.MANUAL    shell script for accessing man page
  78.         /usr/help/ls/options.HLP    help for ls options
  79.         /usr/help/ls/output.HLP        help for ls output format
  80.  
  81.     If you had a simple help for the who command, "who", it would comprise
  82.     the following files at the top level:
  83.  
  84.         /usr/help/who.HLP    help text for who(1)
  85.         /usr/help/who.MANUAL    script for accessing who(1) man page
  86.  
  87. To make help, cd to the directory, and type: 
  88.  
  89.     % make help
  90.  
  91. or
  92.  
  93.     % make install
  94.  
  95. if you are ambitious.  The source for help is one C module, help.c, with a 
  96. header file, help.h.
  97.  
  98. Help uses the more(1) program to read help texts.  Make sure that more
  99. resides on the path specified by the VIEWPROGRAM defined symbol in help.h.
  100.  
  101. SHAR_EOF
  102. fi # end of overwriting check
  103. if test -f 'help.1'
  104. then
  105.     echo shar: will not over-write existing file "'help.1'"
  106. else
  107. cat << \SHAR_EOF > 'help.1'
  108. .TH HELP 1 "29 November 1984"
  109. .UC 4
  110. .SH NAME
  111. help \- user-friendly documentation reader
  112. .SH SYNOPSIS
  113. .B help
  114. [ -dlqCn [ \fIoption-args\fP ] ]\ \ \ [ \fItopic-path\fP ]
  115. .br
  116. .SH DESCRIPTION
  117. .I Help
  118. is a documentation reader based roughly on the VMS(tm) help facility.
  119. Documentation texts are arranged in a tree, and the user reads them with
  120. .I more(1).
  121. .I Help
  122. allows documentation to be treated as an n-tree, the
  123. structure of the help tree is the structure of the directory tree in
  124. which the help files reside.  Further, 
  125. .I help
  126. supports a user-translucent cross-reference facility.
  127. .PP
  128. The help program is invoked from the shell as shown below.
  129. .PP
  130.     help  [ -dlqCn [option-args] ] topic sub-topic sub-sub-topic ...
  131. .PP
  132. .I Topic
  133. may be any top-level help file name.  A 
  134. .I topic-path
  135. is a path of topics and their subtopics, down into the tree.
  136. Topic and subtopic names may be abbreviated, but no wild-cards
  137. are recognized.
  138. If any switches are present they all must follow a single dash,
  139. and be the first option on the command line.
  140. .PP
  141. The options are:
  142. .TP 5
  143. .B d
  144. Next argument is the help root directory.  Default is /usr/help.
  145. .TP 5
  146. .B l
  147. Use list-only mode.  Only the list of subtopics for the command-line
  148. help path is printed.  Interactive mode is not used.
  149. .TP 5
  150. .B q
  151. Use text-only mode.  Only the text of the help path is printed.
  152. Interactive
  153. mode is not used.
  154. .TP 5
  155. .B C
  156. Forces multi-column output of subtopic lists.
  157. This is the default for interactive mode.
  158. .TP 5
  159. .B n
  160. Force one-topic-per-line output of the subtopics lists.
  161. This is the default for list mode.
  162. .PP
  163. The help interactive mode asks the user what help he would like next.
  164. The prompt looks like this:
  165. .PP
  166. Help Topic?
  167. .PP
  168. or like this:
  169. .PP
  170. vi commands deletion subtopic?
  171. .PP
  172.  
  173. Where "vi commands deletion" is the 
  174. .I "topic-path"
  175. followed to reach this prompt.
  176. The path is printed for each prompt, and as a header for each help
  177. text.
  178. .PP
  179. The following commands are meaningful in to the "topic?" prompt:
  180. .TP 5
  181. .B ?
  182. Print current help and subtopic-list again.
  183. .TP 5
  184. .B ".<topic-name>"
  185. Do a UNIX Programmer's Manual lookup (if possible).  If other characters
  186. follow the dot, they are taken as the filename for the .MANUAL file.
  187. (e.g. command '.who' looks for file who.MANUAL)
  188. .TP 5
  189. .B "Return <CR>"
  190. Exit this level of help.  If at top level, goes back to calling
  191. program.
  192. .TP 5
  193. .B "<subtopic-name>"
  194. Read the documentation for this subtopic, or all subtopics for which
  195. this is a legal abbreviation.  Wildcards are not permitted, but a
  196. short abbreviation matches everything longer.
  197. .TP 5
  198. .B "#"
  199. List the available subtopics again.  This lists the subtopics of this
  200. node, and re-prompts the user.  The help text is NOT re-printed.
  201. .TP 5
  202. .B "$<topicname>"
  203. List some information on the files that make up this topic.  Files
  204. that match the abbreviation of `topicname' are looked up with 
  205. .I file(1).
  206. .PP
  207. On 4.2BSD Unix systems, 
  208. .I help
  209. supports a limited form of Twenex-style name-completion.
  210. The ESCape character invokes completion of a topic name, the ^D character
  211. produces an alphabetized list of possible completions.
  212. Completion is called ``limited'' because only a unique abbreviation
  213. can be filled out with the ESCape key (unlike the Twenex \fIcsh\fP).
  214. .sp 1
  215. .PP
  216. .B "HELP FILES"
  217. .PP
  218. Help files are the text and directories of the 
  219. .I help
  220. documentation tree.
  221. There are four kinds of help files: topic directory, help file,
  222. subtopic file, and manual file.
  223. .PP
  224. A topic directory is a directory with the name of the subtopic it
  225. describes (e.g.  /usr/help/ls/options is a topic directory about the
  226. topic of options for 
  227. .I ls(1)).
  228. .PP
  229. A help file is a file of formatted text with the name ".HLP".  It
  230. resides in the topic directory for which it is the description text.
  231. .PP
  232. A subtopic file has the name  "subtopic-name.HLP".  It contains the
  233. text for a subtopic which does not itself have any subtopics.
  234. .PP
  235. A manual file contains the commands to be given to 
  236. .I sh(1)
  237. when the user gives the lookup manual command (.).
  238. This file has name <topic>.MANUAL.  Therefore the manual command
  239. script for the current node is just ".MANUAL".
  240. .br
  241. A manual file resides in the same topic directory as the help text 
  242. whose corresponding
  243. manual it accesses.  
  244. The manual may be a shell script or a binary file, but it must be marked
  245. executable by all users.
  246. .PP
  247. A help
  248. .I "cross-reference file"
  249. has the extension ".XREF".  A cross-reference is not listed on the 
  250. subtopics list, even though it resides in the same directories as the
  251. other help files.
  252. There are two kinds of cross reference files; 
  253. .I direct
  254. references and
  255. .I apology
  256. reference.  An apology reference is just some text, apologizing for the 
  257. lack of documentation on a particular topic.
  258. A direct reference is a one-line file, the first character of which must
  259. be the flag '@'.  The rest of the line is a sequence of words, separated by
  260. spaces, specifing the cross-reference help path.  For instance, the file
  261. /usr/help/lpr.XREF might contain the text:
  262. .PP
  263. \ @ printing lpr
  264. .LP
  265. Therefore, lpr.XREF is a direct cross-reference file.
  266. .sp 1
  267. .PP
  268. The usual way of calling help is to just type 
  269. .sp 1
  270. %
  271. .I help
  272. .PP
  273. in the shell.  This brings up help interactive mode at the top level
  274. of topics.  The user can always abort with ctrl/D.
  275. .SH AUTHOR
  276. Neal Ziring with George Robbert, Washington University in St. Louis.
  277. .SH FILES
  278. /usr/local/bin/help
  279. .br
  280. /usr/help*
  281. .br
  282. /usr/ucb/more
  283. .br
  284. /bin/sh
  285. .SH "SEE ALSO"
  286. more(1), man(1), sh(1), system(2), access(2)
  287. .SH DIAGNOSTICS
  288. Error messages are printed whenever a request is made to get
  289. documentation that does not exists, or is not accessible.  
  290. .PP
  291. Signals are not trapped.
  292. .SH BUGS
  293. The file naming conventions are somewhat strict.
  294. .PP
  295. No wildcards allowed in topic names.  (Could have made them reg. exp.
  296. but that would be confusing to novices.)
  297. .PP
  298. Terminal type is not checked, the display is assumed to be at least 76
  299. columns wide.
  300. .PP
  301. There is no way for the user to specify another program for viewing
  302. files ("more -d" is always used.)  This is coded into the header file.
  303. .PP
  304. The algorithm used for selecting amoung ambiguous help paths is
  305. non-obvious, and can be confusing when too-short abbreviations are used.
  306.  
  307. SHAR_EOF
  308. fi # end of overwriting check
  309. if test -f 'help.h'
  310. then
  311.     echo shar: will not over-write existing file "'help.h'"
  312. else
  313. cat << \SHAR_EOF > 'help.h'
  314.  
  315.     /*
  316.          *    help.h: header file for the VMS-help emulator
  317.      */
  318.  
  319. #include <stdio.h>
  320. #include <ctype.h>
  321. #define BSD
  322. /* #define USG */
  323. #ifdef USG
  324. #include <sys/types.h>
  325. #endif
  326. #include <sys/file.h>
  327. #include <sys/param.h>
  328. #include <sys/dir.h>
  329. #ifdef BSD
  330. #include <sys/ioctl.h>
  331. #include <sys/signal.h>
  332. #endif
  333.  
  334.  
  335. #define iswhite(_c)       (_c ==' ' || _c =='    ' || _c =='\n' || _c =='\r')
  336. #define PROMPT " Topic? "
  337. #define SUBPROMPT "subtopic? "
  338. #define HELPEX    ".HLP"                /* help extension */
  339. #define    MANEX    ".MANUAL"            /* manual filename*/
  340. #define MAN_SUBEX "/.MANUAL"                    /* other man filename */
  341. #define XREFEX  ".XREF"                         /* cross reference */
  342. #define INFOHEAD "file "
  343. #define INFOTAIL " | sed 's/^/     /'"
  344.  
  345. #define LF '\n'
  346. #define RET '\r'
  347. #define ESC '\033'
  348.  
  349. #define    MAXLINELEN    128
  350. #define    MAXNAMELEN     24        /* max length of NAME of topix */
  351. #define    MAXNAMES    256        /* maximum number of subtopics */
  352. #define COLUMNSPACE      3
  353. #define TERMWID         78
  354. #define HELPDIR "/usr/help"
  355. #define VIEWPROGRAM  "/usr/ucb/more"    /* program to look at text */
  356. #define VIEWPROGOPTS1 "-d"
  357. #define VIEWPROGOPTS2 "-18"
  358. #define SHELLPROG    "/bin/sh"        /* program to execute text */
  359. #define SHELLOPTS    "-c"
  360.  
  361. char     progname[MAXNAMELEN];
  362. char    olddir[MAXNAMELEN + 68];
  363. char    newdir[MAXNAMELEN + 68];
  364. char    *helpdir;
  365. char    **environ;    /* environment, for forks */
  366. char    gbl_ppt[MAXLINELEN];
  367.  
  368. int    dumb_flag;
  369. int    list_flag;
  370. int    col_flag;
  371. int    frst_flag;
  372.  
  373. char    *helpcmds[] =
  374.     { "Return - Exit this level of help",
  375.       "*      - Print this message",
  376.       "?      - Reprint this help and its subtopics",
  377.       "#      - Reprint just subtopics of this help",
  378.       ".      - Look at current manual page, if any",
  379.       "$<topic>    - Get information on topic files",
  380.       ".<topic>    - Look at topic manual page, if any",
  381.       "<topic>     - Look at help for subtopic `topic'" ,
  382.       NULL
  383.     };
  384.  
  385. #ifdef BSD
  386. struct tchars  *init_tchars = NULL;
  387. struct tchars  *here_tchars = NULL;
  388.  
  389. #define Set_Tc_Init(FD) \
  390.         ((init_tchars==NULL)?(0):(ioctl(FD,TIOCSETC,init_tchars)))
  391. #define Set_Tc_Here(FD) \
  392.         ((here_tchars==NULL)?(0):(ioctl(FD,TIOCSETC,here_tchars)))
  393.  
  394. int is_tty = 0;
  395. char *read_resp();
  396.  
  397. #define ESC_COMP       1
  398. #define CTRLD_COMP    -1
  399. #define SPEC_CHARS  ".$?*#"
  400.  
  401. char gbl_match[MAXNAMELEN];
  402.  
  403. #else
  404. #define Set_Tc_Init  1
  405. #define Set_Tc_Here  0
  406. #endif
  407. SHAR_EOF
  408. fi # end of overwriting check
  409. if test -f 'help.c'
  410. then
  411.     echo shar: will not over-write existing file "'help.c'"
  412. else
  413. cat << \SHAR_EOF > 'help.c'
  414. #include "help.h"
  415. #define MAXMATCHES    24
  416.  
  417.  /* ************************************************************
  418.   *             H   E   L   P    !!
  419.   *             =========================
  420.   *
  421.   *     This program emulates the VMS help facility in the UNIX
  422.   *  environment.  The main routine HELP1 looks up help and 
  423.   *  subtopics for help.  The help texts for various topix 
  424.   *  are tree-structured.  A directory is defined as a "help"
  425.   *  directory, it has four types of files in it:
  426.   *        main help text:     .HLP
  427.   *        manual page name:    .MANUAL
  428.   *        subtopic texts:        <topicname>.HLP
  429.   *        subtopic directories:    <topicname>
  430.   *             subtopic cross-refs:    <topicname>.XREF
  431.   *
  432.   *    Subtopic names must start with an alphanumeric
  433.   *  character.  Preferably all subtopics will start with
  434.   *  lowercase letters.
  435.   *
  436.   *    The routine help1 is recursive, it descends the    tree
  437.   *  structure.
  438.   */
  439.  
  440.   main(argc, argv)
  441.       int argc;
  442.     char *argv[];
  443.   {
  444.     int i,j,k;
  445.     int longlen;
  446.     char yesno[10];
  447.     char *opts;
  448.         
  449.     strcpy(progname,argv[0]);
  450.     helpdir = NULL;
  451.     getwd(olddir);
  452.     dumb_flag = 0;
  453.     list_flag = 0;
  454.     frst_flag = 1;
  455.     col_flag  = 1;
  456.  
  457.     argc--;
  458.     if (*(*++argv) == '-') {        /* must be options */
  459.         for (opts = *argv; *opts != '\0'; opts++) {
  460.             switch(*opts) {
  461.                case '-' :    
  462.                    break;
  463.  
  464.                case 'd' :    
  465.                    if (argc > 1) {
  466.                     helpdir = *++argv;
  467.                     argc--;
  468.                 }
  469.                 break;
  470.  
  471.                case 'l' :
  472.                    list_flag = 1;
  473.                 col_flag = 0;
  474.                 break;
  475.  
  476.                case 'q' :
  477.                    dumb_flag = 1;
  478.                 break;
  479.  
  480.                case 'C' :
  481.                    col_flag = 1;
  482.                 break;
  483.  
  484.                case 'n' :
  485.                    col_flag = 0;
  486.  
  487.                default:
  488.                     fprintf(stderr,"%s: %c: bad option.\n",
  489.                     progname,*opts);
  490.                 break;
  491.             }
  492.         }
  493.         argc--; argv++;
  494.     }
  495.  
  496.     if (helpdir == NULL) helpdir = HELPDIR;
  497.  
  498.     if (chdir(helpdir) < 0) {
  499.         fprintf(stderr,"%s: %s: help directory not found.\n",
  500.             progname,helpdir);
  501.         exit(1);
  502.     }
  503.  
  504. #ifdef BSD
  505.     init_funky();
  506. #endif
  507.     if (argc >= 1) {        /* treat vector as a help path */
  508. #ifdef DEBUG
  509.         fprintf(stderr," help path vector, argc=%d, *argv=%s.\n",
  510.             argc,*argv);
  511. #endif
  512.         help1( "", argv, 0, 0, 0);
  513.  
  514.     }
  515.     else    help1( "", NULL, 0, 0, 0);
  516.  
  517.     exit(0);
  518.  }
  519.  
  520.  
  521.  
  522.  
  523.  /* **************************************************************
  524.   * printhelp: given a string, pop .HLP on the end and do more(1).
  525.   *
  526.   *    This routine sends a help file to more(1).  A string
  527.   *  is passed in, which is the name of a help.  If the string
  528.   *  is nil, then just use the name HLP.
  529.   *    The return value is -1 if no help file is accessible, 
  530.   *  0 if the more(1) command was called okay with fkoff();
  531.   */
  532.  
  533.   printhelp(hs, path)
  534.       char *hs;
  535.   {
  536.     char filename[MAXNAMELEN], comm[MAXNAMELEN + 20];
  537.  
  538.     if (hs == NULL) strcpy(filename,HELPEX);
  539.     else if (strlen(hs) < 1) strcpy(filename, HELPEX);
  540.     else {
  541.         strcpy(filename, hs);
  542.         strcat(filename,HELPEX);
  543.     }
  544.  
  545.     if ( access(filename, R_OK) < 0 ) {
  546.         printf("\n Sorry, no help text for %s.\n",
  547.             (hs==NULL)?"this topic":hs );
  548.         return(-1);
  549.     }
  550.  
  551.     if (path != NULL) printf("\n HELP: %s\n",path);
  552.  
  553.     fkoff(VIEWPROGRAM,VIEWPROGOPTS1,VIEWPROGOPTS2,filename,NULL);
  554.  
  555.     return(0);
  556.   }
  557.  
  558.  
  559.  
  560. /* *************************************************************
  561.  * printtopix: print the topics available in this directory
  562.  *        in a nice format.
  563.  *
  564.  *    This routine does a directory of help options, 
  565.  *  and prints them out in a manner similar to that of ls(1).
  566.  *  All filenames which start with anything other than numbers 
  567.  *  or letters are not kept.  Extensions are stripped off.
  568.  *    
  569.  *    The number of subtopics found is returned, along with
  570.  *  a pointer to a null-terminated vector of string pointers.
  571.  *  If the mode is non-zero, it means just use the strings stored
  572.  *  in topix.  If mode==0, then re-allocate space and re-check
  573.  *  the directory.  If mode
  574.  */
  575.  
  576.  printtopix( topix, mode, supress)
  577.      char *topix[];
  578.     int mode, supress;
  579. {
  580.     int i,j,k,l,xrefs = 0;
  581.     int  namewidth, totalnames;
  582.     char *malloc(), *nbuf;
  583.     char *thisname, *index();
  584.     char *s1, *s2, **nxtname, *nxcnt;
  585.     struct direct *readdir(), *filedat;
  586.     DIR *dirp, *opendir();
  587.  
  588.     if ( mode ) {
  589.         for(nxtname=topix, i=0; *nxtname++ != NULL; i++ );
  590.         totalnames = i;
  591.         goto inputtopix;
  592.     }
  593.  
  594.     /* if mode is zero, then allocate space and search the directory */
  595.     nbuf = malloc( MAXNAMES * MAXNAMELEN );
  596.     nxcnt=nbuf;
  597.     dirp = opendir(".");
  598.     if (dirp == NULL) {
  599.         fprintf(stderr,"%s: Cannot open help directory.\n",progname);
  600.         return(-1);
  601.     }
  602.  
  603.     for(nxtname=topix, i=0; i < (MAXNAMES-1) ; ) {
  604.         filedat = readdir(dirp);
  605.         if (filedat == NULL) break;
  606.         thisname = filedat->d_name;
  607.         if ( !(isalnum(*thisname)) )    /* if not in [0-9A-Za-z] */
  608.                 continue;    /* do the next one.      */
  609.         *nxcnt = '\0';
  610.         if ( (s1 = index(thisname,'.')) != NULL) {
  611.             if (strcmp(s1, HELPEX) == 0) *s1 = '\0';
  612.             else if ( strcmp(s1, MANEX) == 0) continue;
  613.             else if ( strcmp(s1, XREFEX) == 0) {
  614.                 /* mark xref with at sign in first char */
  615.                 strcpy(nxcnt,"@");
  616.             }
  617.         }
  618.         if (strlen(thisname) >= MAXNAMELEN - 1)
  619.             *(thisname+MAXNAMELEN-1) = '\0';
  620.         /* copy in data from this loop */
  621.         if (*nxcnt == '\0') strcpy(nxcnt,thisname);
  622.         else strcat(nxcnt,thisname);
  623.         *nxtname++ = nxcnt; 
  624.         /* update pointers for next loop */
  625.         nxcnt += strlen(thisname) + ((*nxcnt=='@')?(2):(1));
  626.         i++;
  627.     }
  628.     *nxtname++ = NULL;
  629.     totalnames = i;
  630.     closedir(dirp);
  631.  
  632.     if (totalnames == 0) return(0);
  633.     
  634.     /* sort the names in ascending order with exchange algorithm */
  635.     for(i=0; i < totalnames-1; i++)
  636.         for(j=i+1; j <totalnames; j++)
  637.             if (strcmp(topix[i],topix[j]) > 0) {
  638.                 thisname = topix[i];
  639.                 topix[i] = topix[j];
  640.                 topix[j] = thisname;
  641.             }
  642.         
  643.  
  644.  inputtopix:
  645.     if (supress) return(totalnames);
  646.     else {
  647.         if (col_flag) printf("\n Subtopics:\n");
  648.         printlist(topix, totalnames);
  649.         return(totalnames);
  650.     }
  651.  
  652. }
  653.  
  654.  
  655.  
  656.  /* ****************************************************************
  657.   * help1: descend recursive help tree and provide some
  658.   *         user services.
  659.   *
  660.   *    This routine is the heart of the new UNIX help facility.
  661.   *  It climbs recursively around an n-tree of documentation files
  662.   *  and directories.  The routine printhelp() outputs a file of
  663.   *  help text, the routine printtopix prints out all subtopics.
  664.   *
  665.   *    This routine can operate in interactive mode, or not.  The
  666.   *  basic cycle of operation is:
  667.   *        if (not list-only-mode) print help.
  668.   *        if (not quiet-mode)    print list.
  669.   *        if (not interactive)    return.
  670.   *        print prompt and do commands.
  671.   *        return.
  672.   *
  673.   *    There are a number of commands available in the
  674.   *  interactive mode, they are:
  675.   *
  676.   *        blank line:    up recursive level.
  677.   *        subtopic name:    recurse to subtopic.
  678.   *        ?:        list topics again.
  679.   *        *:        get list of commands
  680.   *        $:        get info on topix
  681.   *        ^D:        quit help program.
  682.   *        .:        man page (if any).
  683.   *        #        list topics again
  684.   *        
  685.   */
  686.  
  687.   help1(ppt,svec,skip,xref,comp_flag)
  688.       char *ppt;
  689.     char *svec[];
  690.     short  skip;
  691.     short  xref;       /* non-zero if doing xref */
  692.     short  comp_flag;  /* non-zero if doing completions */
  693. {                             /* this routine is too big, eh? */
  694.     int i,j,k, err;
  695.     int no_subs;
  696.     char answer[MAXLINELEN];
  697.     char fullpath[MAXNAMELEN + 29];
  698.     char yesno[10];
  699.     char *topix[MAXNAMES], *mx[MAXMATCHES];
  700.     int  matchv();
  701.     char *index(), *getenv();
  702.     char *s1, *s2, *s3, *rest;
  703.     char *wvec[MAXMATCHES];
  704.     char cmdbuf[90];
  705.  
  706.  
  707.     if (comp_flag) {
  708.         printtopix( topix, 0, 1);
  709.         if ( (k = matchv( *svec, topix, mx)) == 0) return(-1);
  710.         /* if there is more than one match, and we are not at end, flub */
  711.         if ( svec[1] != NULL  &&  k > 1 ) return(-1);
  712.         /* if we are not at end, go down one level */
  713.         if ( svec[1] != NULL ) {
  714.         if ( chdir( mx[0] ) < 0) return(-1);
  715.         else {
  716.             k = help1(ppt, svec+1, 0, 0, comp_flag);
  717.             chdir("..");
  718.             return(k);
  719.         }
  720.         }
  721.         /* if we are at end, then do appropriate action on comp_flag */
  722.         else {
  723.         strcpy(gbl_match, mx[0]);
  724.         if ( comp_flag == CTRLD_COMP ) {
  725.             fputs("\n",stdout);
  726.             printlist(mx,k);
  727.         }
  728.         }
  729.         return(0);
  730.     }
  731.  
  732.     if ( svec != NULL  && *svec != NULL) {
  733.         printtopix( topix, 0, 1);
  734.         if ( (k = matchv(*svec, topix, mx)) == 0) {
  735.         printf(" sorry, no direct help path to %s %s\n",ppt,*svec);
  736.         if ( (k = xref_matchv(*svec, topix, mx)) == 0) {
  737.             printf("    no cross-references, either.\n\n");
  738.             if (strlen(ppt) == 0) help1(ppt,NULL,1,0, 0);
  739.             return(-1);
  740.         }
  741.         else {
  742.             do_xref(ppt,*svec,mx);
  743.             if (strlen(ppt) == 0) help1(ppt,NULL,1,0, 0);
  744.             return(-1);
  745.         }
  746.         }
  747.  
  748.         for(i = 0, j = 0, err = -1; mx[i] != NULL; ) {
  749.         if (i > 0)
  750.           j = takeit(" Next help path: %s %s\nTry it?",ppt,mx[i]);
  751.         if (j == 1) { i++; continue; }
  752.         if (j < 0)  break;
  753.         strcpy(fullpath,ppt);
  754.         strcat(fullpath," ");
  755.         strcat(fullpath,mx[i]);
  756.  
  757.         if ( chdir(mx[i]) < 0) {
  758.             if ( *(svec + 1) != NULL ) {
  759.             i++;
  760.             continue;
  761.             }
  762.             if (list_flag) 
  763.             return(-1);
  764.             else
  765.             printhelp( mx[i],fullpath);
  766.             printf("\n");
  767.             if (!dumb_flag) help1(ppt,NULL,1,0, 0);
  768.             err = 0;
  769.         }
  770.         else {
  771.             if ( help1(fullpath,(svec+1),0,0, 0) >= 0) {
  772.             err = 0;
  773.             chdir("..");
  774.             printf("\n");
  775.             if (!dumb_flag) help1(ppt,NULL,1,xref, 0);
  776.             }
  777.             else chdir("..");
  778.         }
  779.         i++;
  780.         }
  781.         return(err);
  782.     }
  783.  
  784.     if ( !list_flag && !skip) 
  785.         if (printhelp(NULL, ppt) < 0) {
  786.         return(-1);
  787.         }
  788.  
  789.     if ( !list_flag && !dumb_flag && ( access(MANEX, R_OK) >= 0) && !skip)
  790.         printf("\n    Manual page IS available.\n");
  791.  
  792.     if ( !dumb_flag ) {
  793.         if ( printtopix( topix, 0, skip) <= 0 ) {
  794.         no_subs = 1;
  795.         }
  796.         else no_subs= 0;
  797.     }
  798.  
  799.     if ( dumb_flag  ||  list_flag ) return(0);
  800.  
  801.     while (xref == 0) {
  802.         if (frst_flag) {
  803.             printf(" (type '*' for commands)\n");
  804.             frst_flag = 0;
  805.         }
  806.  
  807.  
  808.         /* now, prompt and wait for user response */
  809.  
  810.         if ( strlen(ppt) < 1 ) sprintf(gbl_ppt," Help %s",PROMPT);
  811.         else         sprintf(gbl_ppt,"  %s %s",ppt,SUBPROMPT);
  812.         fputs(gbl_ppt,stdout);
  813. #ifndef BSD
  814.         if ( fgets(answer, MAXLINELEN-1, stdin) == NULL ) {
  815.             printf("\nbye...\n");
  816.             exit(1);
  817.         }
  818. #else
  819.         if (read_gets(stdin->_file, answer, MAXLINELEN - 1, 1) < 0) {
  820.                 printf("\nbye...\n");
  821.             exit(1);
  822.         }
  823. #endif
  824.  
  825.         /* first remove any leading blanks or tabs */
  826.         for(s1=answer; *s1 == ' ' || *s1 == '    '; s1++);
  827.  
  828.         /* chop off all of answer after first word. */
  829.         s2 = index(s1,' ');
  830.         if (s2 == NULL)  s2 = index(s1,'    ');
  831.         if (s2 == NULL)  s2 = index(s1,'\n');
  832.         if (s2)
  833.           { *s2 = '\0'; rest = ++s2; }
  834.         else {
  835.             rest = s2 = s1 + strlen(s1);
  836.         }
  837.         makewvec(rest,wvec,MAXMATCHES);
  838.  
  839.         if ( strlen(s1) == 0 )        /*  on blank line, */
  840.             break;            /* pop up one level*/
  841.  
  842.         switch (*s1) {
  843.                 case '?':            /* ?: print stuff again */
  844.                 printhelp(NULL, ppt);
  845.             if ( access(MANEX, R_OK) >= 0 )
  846.                 printf("\n    Manual page IS available.\n");
  847.                 if (!no_subs) printtopix(topix, 1,0);
  848.             else    printf("\n Sorry, no subtopics.\n\n");
  849.             break;
  850.  
  851.             case '#':
  852.             if (!no_subs) printtopix( topix, 1,0);
  853.             else    printf("\n Sorry, no subtopics.\n\n");
  854.             break;
  855.  
  856.             case '*':            /* *: list commands */
  857.             printf("\n");
  858.                 for(i=0; helpcmds[i] != NULL; i++) {
  859.                 printf("    %s\n",helpcmds[i]);
  860.             }
  861.             printf("\n");
  862.             break;
  863.  
  864.             case '$':            /* $: find out about files */
  865.                 s2 = s1 + 1;
  866.             if (no_subs) {
  867.                 printf("\n Sorry, no subtopics.\n\n");
  868.                     break;
  869.             }
  870.             strcpy(cmdbuf, INFOHEAD);
  871.             strcat(cmdbuf,s2);
  872.             strcat(cmdbuf,"* ");
  873.             strcat(cmdbuf, INFOTAIL);
  874.             printf("\n File information: \n");
  875.             system(cmdbuf);
  876.             printf("\n");
  877.             break;
  878.                 
  879.  
  880.                 case '.':            /* .: do manpage if any */
  881.                 s2 = s1 + 1;
  882.             if (no_subs) {
  883.                 printf("\n Sorry, no subtopics.\n\n");
  884.                     break;
  885.             }
  886.             if ( *s2 == '\0' ) {
  887.                 mx[0] = "";  mx[1] = NULL; k = 1;
  888.             }
  889.             else if ( (k = matchv( s2, topix, mx)) == 0 ) {
  890.                printf("\n Sorry, no topics match %s\n",s2);
  891.                printf(" (list cmnds with '*', topix with '#')\n");
  892.             }
  893.             for( i=0; i < k; i++ ) {
  894.                 strcpy(cmdbuf,mx[i]);
  895.                 strcat(cmdbuf,MANEX);
  896.                 if (access(cmdbuf, R_OK|X_OK ) < 0) {
  897.                         strcpy(cmdbuf,mx[i]);
  898.                     strcat(cmdbuf,MAN_SUBEX);
  899.                     if (access(cmdbuf,R_OK|X_OK) < 0) {
  900.                         printf("\n Sorry, %s for %s.\n\n",
  901.                           "No manual reference available",
  902.                           (strlen(s2)==0)?ppt:mx[i]);
  903.                         continue;
  904.                     }
  905.                 }
  906.                 if (i > 0) {
  907.                       k=
  908.                      takeit(" Next man page: %s.\n Take it? ",
  909.                     mx[i]);
  910.                      if (k ==  1) continue;
  911.                      if (k == -1) break;
  912.                 }
  913.                 printf(" ...doing %s\n\n",cmdbuf);
  914.                 fkoff(SHELLPROG,SHELLOPTS,cmdbuf,NULL);
  915.                     /* source contents of .MANUAL file */
  916.             }
  917.             break;
  918.  
  919.  
  920.             default:            /* must be a topic spec */
  921.                 if ( no_subs )  {
  922.                 printf("\n Sorry, no subtopics.\n\n");
  923.                     break;
  924.             }
  925.             if ( (k = matchv( s1, topix, mx)) == 0 ) {
  926.                printf("\n Sorry, no direct help for  %s\n",s1);
  927.                if ( (k = xref_matchv(s1,topix,mx)) == 0) {
  928.                    printf(" no cross-references to  %s\n",s1);
  929.                    printf(
  930.                 " (list cmnds with '*', topics with '#')\n");
  931.                    break;    /* leave switch */
  932.                }
  933.                else {
  934.                    do_xref(ppt,s1,mx);
  935.                    break;   /* leave switch */
  936.                }
  937.             }
  938.  
  939.             /* step thru sub-topics that match src */
  940.                 for(i=0; i < k; i++) {
  941.                 s3 = mx[i];
  942.  
  943.                 if (i > 0) {
  944.                     j=takeit("\nNext %s subtopic: %s\nTake it?",
  945.                      ppt,s3);
  946.                 if (j ==  1) continue;
  947.                 if (j == -1) break;
  948.                 }
  949.  
  950.                 if ( chdir(s3) >= 0 ) {  /* directory  subtopic */
  951.                     strcpy(fullpath,ppt);
  952.                     strcat(fullpath," ");
  953.                     strcat(fullpath,s3);
  954.                     help1(fullpath,wvec,0,0, 0);    /* recurse */
  955.                 if ( strcmp(getwd(newdir),helpdir) )
  956.                     chdir("..");
  957.                 }            /* else text subtopic */
  958.                 else  {
  959.                 strcpy(fullpath, ppt);
  960.                 strcat(fullpath, " ");
  961.                 strcat(fullpath, s3);
  962.                 printhelp(s3, fullpath);
  963.                 }
  964.             }
  965.  
  966.         }    /* end of switch */
  967.  
  968.     }    /* end of while(1) */
  969.  
  970.     if (!no_subs && (*topix != NULL) ) free( *topix );
  971.  
  972.     return(0);
  973.  
  974.   }    /* end of help1 */
  975.  
  976.  
  977. /* ****************************************************************
  978.  * takeit: ask user whether to take next topic, return t or f
  979.  *
  980.  *    This routine takes a message, with format, and asks the 
  981.  *  user whether he wants to do the action, not do the action,
  982.  *  or quit the cycle of actions.
  983.  *    y - return  0
  984.  *    n - return  1
  985.  *    q - return -1
  986.  *    ? - tell what y, n, and q do.
  987.  *      * - tell what y, n, and q do.
  988.  *     <CR> - carriage return
  989.  *
  990.  *    Naturally, case is insignificant.
  991.  */
  992.  
  993.  takeit(fmt,m1,m2,m3)
  994.    char *fmt, *m1, *m2, *m3;
  995.  {
  996.  
  997.     char ans[40];
  998.     int  done, ret;
  999.     char *fgets();
  1000.  
  1001.     for(done = 0; !done; ) {
  1002.         if (fmt != NULL) printf(fmt, m1, m2, m3);
  1003.         printf(" [ynq] ");
  1004.         if (fgets(ans, 39, stdin) == NULL) exit(1);  /* abort */
  1005.         isupper(*ans)?(*ans=tolower(*ans)):0;
  1006.         if (*ans == 'n') {
  1007.             done = 1;
  1008.             ret = 1;
  1009.         }
  1010.         else if (*ans == 'y' || *ans == '\n') {
  1011.             done = 1;
  1012.             ret = 0;
  1013.         }
  1014.         else if (*ans == 'q') {
  1015.             done = 1;
  1016.             ret = -1;
  1017.         }
  1018.         else printf("  Answer y to get next subtopic, n to skip, q to quit.\n");
  1019.     }
  1020.     return(ret);
  1021.  }
  1022.  
  1023.  
  1024.  /* ***************************************************************
  1025.   * matchv: return all matches of a string in a vector.
  1026.   *
  1027.   *    This routine accepts a string and a vector of strings. 
  1028.   *  The string is supposed to be an abbreviation of one or more
  1029.   *  strings in the vector.  The full versions of the strings are
  1030.   *  placed in a another vector (which is in a static area) and
  1031.   *  a pointer to that vector returned.  Note that the input
  1032.   *  vector of pointers must have NULL as its terminating element.
  1033.   *  The output vector will also terminate with NULL.
  1034.   *    NOTE: The vector returned contains pointers into the input
  1035.   *  vector.  Do not, therefore, mess with the contents of the output
  1036.   *  vector.
  1037.   *
  1038.   *  If NULL is returned, nothing matched, or there was some other
  1039.   *  error.
  1040.   */
  1041.  
  1042.   matchv( src, vec, mx)
  1043.     char *src;
  1044.     char *vec[];
  1045.     char *mx[];
  1046.   {
  1047.     char *m[MAXMATCHES];
  1048.     char  *s1, *s2;
  1049.     int i,j, slen;
  1050.  
  1051.     if ( (slen = strlen(src)) == 0 ) return(NULL);  
  1052.  
  1053.     for(i=0, j=0; vec[i] != NULL && j < MAXMATCHES; i++) {
  1054.         if  ( strcmp(src,vec[i]) == 0 ) {   /* exact match! */
  1055.         m[0] = vec[i];  j = 1;
  1056.         break;
  1057.         }
  1058.         else if ( strncmp(src,vec[i],slen) == 0   && 
  1059.              strlen(vec[i]) >= slen )
  1060.                    m[j++] = vec[i];
  1061.     }
  1062.     m[j] = NULL;
  1063.     for(i=0; i <= j; mx[i] = m[i], i++);
  1064.     return(j);
  1065.   }
  1066.  
  1067.  /* *****************************************************************
  1068.   * fkoff: fork a process and return the exit status of the process.
  1069.   *
  1070.   *    This routine takes a command line separated into words, and
  1071.   *  uses vfork(2) and execve(2) to quickly run the program.
  1072.   *
  1073.   *  This is a simplified version of the fkoff() routine from dcon(8).
  1074.   *  Here, a program name and up to four arguments may be passed in.
  1075.   *  If one of them is null, FINE, but the fifth 
  1076.   *
  1077.   */
  1078.  
  1079.   fkoff(prg,arg1,arg2,arg3,arg4)
  1080.     char *prg, *arg1, *arg2, *arg3, *arg4;
  1081.  
  1082.   {
  1083.     char *command, *malloc(), *argvec[6];
  1084.     int pid, stat, i;
  1085.  
  1086.     for(i=0; i < 6; argvec[i++] = NULL);
  1087.     argvec[0] = prg;
  1088.     argvec[1] = arg1;
  1089.     argvec[2] = arg2;
  1090.     argvec[3] = arg3;
  1091.     argvec[4] = arg4;
  1092.     if (argvec[0] == NULL) return(-1);
  1093.     command = malloc( strlen(argvec[0]) + 1);
  1094.     strcpy(command,argvec[0]);
  1095.  
  1096.     Set_Tc_Init( (stdin->_file) ) ;
  1097. #ifdef USG
  1098.     pid = fork();
  1099. #else
  1100.     pid = vfork();    /* fork 2 copies of us */
  1101. #endif
  1102.     if (pid < 0 )  {
  1103.             fflush(stdout);
  1104.         perror("help: fork");
  1105.         Set_Tc_Here( (stdin->_file) ) ; 
  1106.         return(0);
  1107.     }
  1108.     else if (pid == 0)  { /* we are child, execve the program. */
  1109.            pid = execve(command,argvec,environ);
  1110.         _exit(1);   
  1111.     }
  1112.     else {          /* we are parent, wait for child */
  1113.         pid = wait(&stat);
  1114.         Set_Tc_Here( (stdin->_file) ) ;
  1115.         if (pid == -1) return(pid);
  1116.         stat = stat / 0400;   /* get hi byte of status */
  1117.         return(stat);
  1118.         }
  1119.   }
  1120.  
  1121. /* **************************************************************
  1122.  * makewvec: make a vector of words, up to N of them
  1123.  *
  1124.  *    This routine uses index to simply parse a string
  1125.  *  made up of (possibly) several blank-separated words.
  1126.  *  The words are stored into the slots of a vector, as pointers
  1127.  *  into the original string.  Therefore, the original string
  1128.  *  is destroyed.
  1129.  */
  1130.  makewvec(sstr, rvec, veccnt)
  1131.      char *sstr;
  1132.      char *rvec[];
  1133.      int veccnt;
  1134. {
  1135.     int mcnt = 0;
  1136.     int done = 0;
  1137.     int i,j;
  1138.     char *s1, *s2, *index();
  1139.  
  1140.     if (strlen(sstr) == 0) {
  1141.     rvec[0] = NULL;
  1142.     }
  1143.     else {
  1144.     /* skip leading whitespace */
  1145.     for(s1=sstr; iswhite(*s1); s1++);
  1146.     for(mcnt = 0, done = 0; !done; mcnt++) {
  1147.         s2 = index(s1,' ');
  1148.         if (s2 == NULL) s2 = index(s1,'    ');
  1149.         if (s2 == NULL) s2 = index(s1,'\n');
  1150.         if (s2 != NULL) *s2 = '\0';
  1151.         rvec[mcnt] = s1;
  1152.         if (mcnt + 1 >= veccnt) break;
  1153.         if (s2 == NULL) done = 1;
  1154.         else {
  1155.         /* skip more white space */
  1156.         for(s1 = s2+1; iswhite(*s1); s1++);
  1157.         if (*s1 == '\0') done = 1;
  1158.         }
  1159.     }
  1160.     rvec[mcnt] = NULL;
  1161.     }
  1162.  
  1163.     return(mcnt);
  1164. }
  1165.  
  1166.  
  1167.  /* ***************************************************************
  1168.   * xref_matchv: return all cross-ref matches for a topic
  1169.   *
  1170.   *    This routine accepts a string and a vector of strings. 
  1171.   *  The string is supposed to be a referal to one or more
  1172.   *  cross-reference file names in the vector.  The matches are
  1173.   *  placed in a another vector (which is in a static area) and
  1174.   *  a pointer to that vector returned.  Note that the input
  1175.   *  vector of pointers must have NULL as its terminating element.
  1176.   *  The output vector will also terminate with NULL.
  1177.   *    NOTE: The vector returned contains pointers into the input
  1178.   *  vector.  Do not, therefore, mess with the contents of the output
  1179.   *  vector.  
  1180.   *     NOTE: cross-ref file names in the topix vector begin with 
  1181.   *  the flag char '@' and still have their extension of .XREF.
  1182.   *
  1183.   *  If 0 is returned, nothing matched, or there was some other error.
  1184.   */
  1185.  
  1186.   xref_matchv( src, vec, mx)
  1187.     char *src;
  1188.     char *vec[];
  1189.     char *mx[];
  1190.   {
  1191.     char *m[MAXMATCHES];
  1192.     char  *s1, *s2;
  1193.     char  one_ref[MAXNAMELEN];
  1194.     int i,j, slen;
  1195.  
  1196.     if ( (slen = strlen(src)) == 0 ) return(NULL);  
  1197.  
  1198.     for(i=0, j=0; vec[i] != NULL && j < MAXMATCHES; i++) {
  1199.         if ( *vec[i] != '@' ) break;
  1200.         for(s1 = vec[i], s2 = one_ref; 
  1201.         *s1 != '\0' && *s1 != '.';
  1202.         *s2++ = *s1++);
  1203.         *s2 = '\0';
  1204.         if ( strcmp(src,one_ref) == 0) {    /* exact match! */
  1205.         m[0] = vec[i]; j = 1;
  1206.         break;
  1207.         }
  1208.         else if
  1209.           (strncmp(src,(vec[i] + 1),slen) == 0 && strlen(vec[i]) >= slen)
  1210.               m[j++] = vec[i];
  1211.         }
  1212.     m[j] = NULL;
  1213.     for(i=0; i <= j; mx[i] = m[i], i++);
  1214.     return(j);
  1215.   }
  1216.  
  1217.  
  1218. /* **************************************************************
  1219.  * do_xref: given a vector of cross-reference filenames, do them
  1220.  *
  1221.  *     This routine follows a set of cross references.  
  1222.  *   Technically, there are two kinds of cross-reference files, 
  1223.  *   distinguished by the first character of their contents.
  1224.  *   If the first character is an at sign (@) the file is a 
  1225.  *   ``direct'' cross-reference and the rest of the line starting
  1226.  *   with the @ sign is a vector of words that give an ABSOLUTE
  1227.  *   help path.  If the first char is not @ then the file is an
  1228.  *   ``apologetic'' cross-reference, and the text in it is a 
  1229.  *   lame-brained excuse for why the topic in question is not
  1230.  *   documented in help.
  1231.  *
  1232.  *      For each filename in the vector, the following actions 
  1233.  *   are performed.
  1234.  *
  1235.  *        1. check that the file exists and is readable, by
  1236.  *           opening it for reading.
  1237.  *        2. If this is not the first xref, ask the user if
  1238.  *           he wants to look at this one, if so, proceed to 
  1239.  *        3. Read the first line of the file and check it.
  1240.  *               a. if first char is not '@', do a more(1)
  1241.  *                  on the file with fkoff.
  1242.  *               b. otherwise, read the first line, and
  1243.  *                  use makewvec to parse it into words.
  1244.  *                  pass these words to a new invokation
  1245.  *                  of help1.  Make the ppt for help1
  1246.  *                  be something that denotes a cross-ref.
  1247.  */
  1248.  do_xref(ppt,src,mx)
  1249.      char *ppt, *src;
  1250.      char *mx[];
  1251. {
  1252.     int i,j,k;
  1253.     char one_ref[MAXNAMELEN];
  1254.     char *s1, *index();
  1255.  
  1256.     for(i=0; mx[i] != NULL; i++) {
  1257.     strcpy(one_ref,mx[i]+1);
  1258.     if (i > 0) {
  1259.         j = takeit("\nNext %s %s cross-reference: %s\nTake it?",
  1260.                ppt,src,one_ref);
  1261.         if (j == 1) continue;
  1262.         if (j == -1) break;
  1263.     }
  1264.     do_one_xref(one_ref,src,ppt);
  1265.     }
  1266.  
  1267.     putchar('\n');
  1268.     return(0);
  1269. }
  1270.  
  1271.     
  1272. /* **************************************************************
  1273.  * do_one_xref: evaluate and do a single cross-reference
  1274.  *
  1275.  *      This routine accepts a single file name and src name
  1276.  *   for a single cross-reference.  It then checks the file
  1277.  *   for type, and performs the appropriate action.
  1278.  *
  1279.  *      If the file is not accessible, an error message is 
  1280.  *   printed.
  1281.  */
  1282.  do_one_xref(xref_file,src,ppt)
  1283.      char *xref_file, *src, *ppt;
  1284. {
  1285.     int i,j;
  1286.     FILE *xr_fp, *fopen();
  1287.     char *wvec[MAXMATCHES];
  1288.     char *index(), *s1, lbuf[MAXLINELEN], xbuf[MAXNAMELEN+15];
  1289.  
  1290.     if ( (xr_fp = fopen(xref_file,"r")) == NULL) {
  1291.     printf(" Sorry, %s cross-reference file inaccessible.\n",xref_file);
  1292.     return(-1);
  1293.     }
  1294.  
  1295.     if (fgets(lbuf,MAXLINELEN,xr_fp) != NULL) {
  1296.     if ( *lbuf != '@' ) {
  1297.         s1 = index(xref_file,'.');
  1298.         if (s1 != NULL) *s1 = '\0';
  1299.         printf(" Cross reference text called \"%s\" is available\n",
  1300.            xref_file);
  1301.         if (ppt != NULL)
  1302.           printf("\n HELP: %s <<%s>>\n",ppt,xref_file);
  1303.         if (s1 != NULL) *s1 = '.';
  1304.         fclose(xr_fp);
  1305.         fkoff(VIEWPROGRAM,VIEWPROGOPTS1,VIEWPROGOPTS2,xref_file,NULL);
  1306.     }
  1307.     else {
  1308.         char pptbuf[MAXNAMELEN+13], pathbuf[256];
  1309.         makewvec(lbuf+1,wvec,MAXMATCHES);
  1310.         printf("\n following cross-reference `help");
  1311.         for(i=0; wvec[i] != NULL; i++) printf(" %s",wvec[i]);
  1312.         s1 = index(xref_file,'.');
  1313.         if (s1 != NULL) *s1 = '\0';
  1314.         printf("' to find help for %s\n\n",xref_file);
  1315.         sprintf(pptbuf,"<<%s>> ",xref_file);
  1316.         if (s1 != NULL) *s1  = '.';
  1317.         getwd(pathbuf);
  1318.         chdir(helpdir);
  1319.         help1(pptbuf,wvec,1,1, 0);
  1320.         chdir(pathbuf);
  1321.         putchar('\n');
  1322.     }
  1323.     }
  1324.     else {
  1325.     printf(" Sorry, cross-ref file %s seems to be empty!\n\n",xref_file);
  1326.     }
  1327.     return(0);
  1328. }
  1329.  
  1330.  
  1331. /* **************************************************************
  1332.  * printlist - print a list of strings in columnar or list form
  1333.  *
  1334.  *     This routine takes a list of names in a vector and prints
  1335.  *   the list according to the current help mode.  This code
  1336.  *   used to be in printtopix() but I decided to move it here
  1337.  *   so it could be used for other things.
  1338.  */
  1339. printlist(vec,namecnt)
  1340.      char *vec[];
  1341.      int namecnt;
  1342. {
  1343.     int i,j,k;
  1344.     int longlen, namewidth, rowcnt, colcnt, xrefs;
  1345.     char *s1, *s2;
  1346.     static char row[TERMWID+1];
  1347.  
  1348.     longlen = xrefs = 0;
  1349.     for(i=0; i < namecnt; i++ ) {
  1350.             if (*vec[i] == '@') xrefs++;
  1351.         else
  1352.           longlen = ((k=strlen(vec[i]))>longlen)?k:longlen;
  1353.     }
  1354.  
  1355.     /* here print the names out in nice columns */
  1356.     namewidth = longlen + COLUMNSPACE;
  1357.     rowcnt = TERMWID / namewidth;
  1358.     colcnt = (namecnt + (rowcnt-1)) / rowcnt ;
  1359.     if (colcnt <= 0) colcnt = 1;
  1360.  
  1361.     if (col_flag && rowcnt >= 1) {
  1362.             printf("\n");
  1363.         for(i=0; i < colcnt ; i++ ) {
  1364.             for(k=0; k < TERMWID; row[k++] = ' ');
  1365.             row[k] = '\0';
  1366.             for(j=0, s1 = row; 
  1367.                     (i+j+xrefs) < namecnt; 
  1368.                     j += colcnt) {
  1369.                 row[strlen(row)] = ' ';
  1370.                 strcpy(s1,vec[i+j+xrefs]);
  1371.                 s1 = s1 + namewidth;
  1372.             }
  1373.             printf("    %s\n",row);
  1374.         }
  1375.         printf("\n");
  1376.     }
  1377.     else {
  1378.         for(i=xrefs; i < namecnt; i++)
  1379.             printf("%s\n",vec[i]);
  1380.      }
  1381.  
  1382. }
  1383.  
  1384. #ifdef BSD
  1385.  
  1386. /* ****************  funky keyboard stuff below here  ************* */
  1387.  
  1388. /* **************************************************************
  1389.  * pushback - push a string back onto the tty input queue
  1390.  *
  1391.  *     This routine accepts a string and pushes it onto the tty
  1392.  *   input queue, as if the user had typed it.
  1393.  *   The input is a file descriptor that points to a tty, and
  1394.  *   a pointer to the string.
  1395.  */
  1396. pushback(fd, str)
  1397.      int fd;
  1398.      char *str;
  1399. {
  1400.     char *s1;
  1401.  
  1402.     for(s1=str; *s1; ioctl( fd, TIOCSTI, s1++));
  1403. }
  1404.  
  1405.  
  1406. /* **************************************************************
  1407.  * getpending - get pending chars from tty, then push them back
  1408.  *
  1409.  *     This routine grabs chars pending on a specified file
  1410.  *   descriptor, puts them into the specified buffer, and pushes
  1411.  *   them back onto the tty input queue.
  1412.  *   The buffer passed by the caller had BETTER be long enough,
  1413.  *   or everything gets munched.
  1414.  *   If the parameter no_pushback is non-zero, the characters are
  1415.  *   not pushed back.
  1416.  *   The number of characters obtained is returned.
  1417.  */
  1418. getpending(fd, buf, buflen, no_pushback)
  1419.      int fd;
  1420.      char *buf;
  1421.      int buflen, no_pushback;
  1422. {
  1423.     int i,j;
  1424.     char x = '\n';
  1425.  
  1426.     ioctl(fd, FIONREAD, &i);
  1427.     if (i <= 0) return(0);
  1428.     j = read(fd, buf, buflen);
  1429.     buf[j-1] = '\0';
  1430.     for(i = j-2; buf[i] > '\030' ; buf[i--] = '\0' );
  1431.     if (i <= 0) return(0);
  1432.     if ( !no_pushback) pushback(fd, buf);
  1433.     return(i);
  1434. }
  1435.  
  1436.  
  1437. /* **************************************************************
  1438.  * init_funky - initialize funky stuff, and save startup values
  1439.  *
  1440.  *         This routine starts up breaking for ESC, and
  1441.  *   saves the initial tchars structure.
  1442.  */
  1443. init_funky()
  1444. {
  1445.     char *malloc();
  1446.  
  1447.     if ( isatty(0) ) {
  1448.     is_tty = 1;
  1449.     init_tchars = (struct tchars *) malloc( sizeof(struct tchars));
  1450.     here_tchars = (struct tchars *) malloc( sizeof(struct tchars));
  1451.     ioctl(0,TIOCGETC,init_tchars);
  1452.     ioctl(0,TIOCGETC,here_tchars);
  1453.     here_tchars->t_brkc = '\033';
  1454.     ioctl(0,TIOCSETC,here_tchars);
  1455.     }
  1456.     
  1457.     return;
  1458. }
  1459.  
  1460.  
  1461. /* **************************************************************
  1462.  * read_gets: do a gets by read(2)ing a line
  1463.  *
  1464.  *       Read a string into a buffer, from the given file
  1465.  *    descriptor.  There are four arguments:
  1466.  *
  1467.  *        fd    file descriptor (usually 0)  
  1468.  *          buf     char * to buffer
  1469.  *         blen    buffer length in bytes
  1470.  *        spec    do special ^[ and ^D processing
  1471.  *
  1472.  *    If the special processing is enabled, ESC invokes helpname
  1473.  *    completion, and ^D invokes possible completion listing.
  1474.  *    When the user finally inputs a line that does not require
  1475.  *    special processing, it is returned as a string to caller.
  1476.  *    If anything goes wrong, or a true EOF is given, -1 is
  1477.  *    returned.
  1478.  *    When special processing is not enabled, the input line is 
  1479.  *    simply returned to the caller as a string.
  1480.  */
  1481. read_gets(fd, buf, blen, do_special)
  1482.      int fd, blen, do_special;
  1483.      char *buf;
  1484. {
  1485.     char *s1, *s2, endc;
  1486.     static  char bufcopy[MAXLINELEN];
  1487.     static  char *wvec[MAXMATCHES];
  1488.     int i,j,k;
  1489.  
  1490.  
  1491.     for(endc = '\0'; endc != LF ; ) {
  1492.     fflush(stdout);
  1493.     i = read(fd, buf, blen);
  1494.  
  1495.     /* now, having read data, analyze it for special stuff */
  1496.     if (i <= 0) return(-1);
  1497.     else {
  1498.         s1 = buf + (i - 1);
  1499.         if (*s1 == ESC || *s1 == LF || *s1 == RET) {
  1500.         endc = (*s1 == ESC)?(*s1):(LF);
  1501.         *s1 = '\0';
  1502.         }
  1503.         else {
  1504.         *++s1 = '\0';
  1505.         endc = *s1;
  1506.         }
  1507.         for(s1 = buf + (strlen(buf) - 1);
  1508.         *s1 == ' '  ||   *s1 == '    ';
  1509.         *s1-- = '\0');
  1510.         for(s1 = buf; *s1 == ' ' || *s1 == '    '; s1++);
  1511.  
  1512.         if (endc == LF || endc == RET || do_special == 0) return(i-1);
  1513.         else {
  1514.         *gbl_match = '\0';
  1515.  
  1516.         /* if this is a non-topic command, just beep */
  1517.         if ( index( SPEC_CHARS, *s1) ) {
  1518.             fputs("  ",stdout);
  1519.             for(s1 = buf; *s1; s1++) fputs("",stdout);
  1520.             fflush(stdout);
  1521.             pushback(stdin->_file,buf);
  1522.             continue;
  1523.         }
  1524.         else
  1525.         /* do special stuff here */
  1526.         if (endc == ESC) {
  1527.             strcpy(bufcopy,buf);
  1528.             i = makewvec(bufcopy, wvec, MAXMATCHES);
  1529.             help1("", wvec, 0, 0, ESC_COMP);
  1530.             fputs("  ",stdout);
  1531.             for(s1 = buf; *s1; s1++) fputs("",stdout);
  1532.             fflush(stdout);
  1533.             pushback(stdin->_file,buf);
  1534.             if ( *gbl_match ) {
  1535.             pushback(stdin->_file,(gbl_match + strlen(wvec[i-1])));
  1536.             }
  1537.             else fputs("",stdout);
  1538.             fflush(stdout);
  1539.         }
  1540.         else
  1541.           if (endc == '\0') {   /* must have been eof */
  1542.               strcpy(bufcopy,buf);
  1543.               i = makewvec(bufcopy, wvec, MAXMATCHES);
  1544.               help1("" , wvec, 0, 0, CTRLD_COMP);
  1545.               printf("\n%s ",gbl_ppt);
  1546.               if (*gbl_match == '\0') fputs("", stdout);
  1547.               fflush(stdout);
  1548.               pushback(stdin->_file,buf);
  1549.  
  1550.           }
  1551.         }
  1552.     }
  1553.     }
  1554. }
  1555.         
  1556.  
  1557.  
  1558. #endif 
  1559. SHAR_EOF
  1560. fi # end of overwriting check
  1561. #    End of shell archive
  1562. exit 0
  1563.  
  1564.  
  1565.